namespace WP7Contrib.Services.BingMaps.Model
{
    using System;
    using System.Device.Location;
    using System.Linq;
    using Collections;
    using Common;
    using Common.Extensions;

    public class ItineraryItem : BaseModel, ICloneable<ItineraryItem>
    {
        private string compassDirection;
        private string exit;
        private string iconType;
        private RouteInstruction instruction;
        private GeoCoordinate maneuverPoint;
        private string sideOfStreet;
        private TimeSpan time;
        private string tollZone;
        private string towardsRoadName;
        private int transitStopId;
        private string transitTerminus;
        private double? travelDistance;
        private double? travelDuration;
        private string travelMode;

        private ObservableCollection<RouteDetail> details;
        private ObservableCollection<ItineraryItem> childItineraryItems;
        private ObservableCollection<RouteHint> hints;
        private ObservableCollection<string> signs;
        private ObservableCollection<RouteTransitLine> transitLine;
        private ObservableCollection<RouteWarning> warnings;

        public ItineraryItem()
        {
            this.details = new ObservableCollection<RouteDetail>();
            this.childItineraryItems = new ObservableCollection<ItineraryItem>();
            this.hints = new ObservableCollection<RouteHint>();
            this.signs = new ObservableCollection<string>();
            this.transitLine = new ObservableCollection<RouteTransitLine>();
            this.warnings = new ObservableCollection<RouteWarning>();
        }

        public string CompassDirection
        {
            get
            {
                return this.compassDirection;
            }
            set
            {
                this.SetPropertyAndNotify(ref this.compassDirection, value, () => this.CompassDirection);
            }
        }

        public ObservableCollection<RouteDetail> Details
        {
            get
            {
                return this.details;
            }
            set
            {
                this.SetPropertyAndNotify(ref this.details, value, () => this.Details);
            }
        }

        public ObservableCollection<ItineraryItem> ChildItineraryItems
        {
            get
            {
                return this.childItineraryItems;
            }
            set
            {
                this.SetPropertyAndNotify(ref this.childItineraryItems, value, () => this.ChildItineraryItems);
            }
        }

        public string Exit
        {
            get
            {
                return this.exit;
            }
            set
            {
                this.SetPropertyAndNotify(ref this.exit, value, () => this.Exit);
            }
        }

        public ObservableCollection<RouteHint> Hints
        {
            get
            {
                return this.hints;
            }
            set
            {
                this.SetPropertyAndNotify(ref this.hints, value, () => this.Hints);
            }
        }

        public string IconType
        {
            get
            {
                return this.iconType;
            }
            set
            {
                this.SetPropertyAndNotify(ref this.iconType, value, () => this.IconType);
            }
        }

        public RouteInstruction Instruction
        {
            get
            {
                return this.instruction;
            }
            set
            {
                this.SetPropertyAndNotify(ref this.instruction, value, () => this.Instruction);
            }
        }

        public GeoCoordinate ManeuverPoint
        {
            get
            {
                return this.maneuverPoint;
            }
            set
            {
                this.SetPropertyAndNotify(ref this.maneuverPoint, value, () => this.ManeuverPoint);
            }
        }

        public string SideOfStreet
        {
            get
            {
                return this.sideOfStreet;
            }
            set
            {
                this.SetPropertyAndNotify(ref this.sideOfStreet, value, () => this.SideOfStreet);
            }
        }

        public ObservableCollection<string> Signs
        {
            get
            {
                return this.signs;
            }
            set
            {
                this.SetPropertyAndNotify(ref this.signs, value, () => this.Signs);
            }
        }

        public string SignsAsCsv
        {
            get { return string.Join(",", this.signs.ToArray()); }
        }

        public TimeSpan Time
        {
            get
            {
                return this.time;
            }
            set
            {
                this.SetPropertyAndNotify(ref this.time, value, () => this.Time);
            }
        }

        public string TollZone
        {
            get
            {
                return this.tollZone;
            }
            set
            {
                this.SetPropertyAndNotify(ref this.tollZone, value, () => this.TollZone);
            }
        }

        public string TowardsRoadName
        {
            get
            {
                return this.towardsRoadName;
            }
            set
            {
                this.SetPropertyAndNotify(ref this.towardsRoadName, value, () => this.TowardsRoadName);
            }
        }

        public ObservableCollection<RouteTransitLine> TransitLine
        {
            get
            {
                return this.transitLine;
            }
            set
            {
                this.SetPropertyAndNotify(ref this.transitLine, value, () => this.TransitLine);
            }
        }

        public int TransitStopId
        {
            get
            {
                return this.transitStopId;
            }
            set
            {
                this.SetPropertyAndNotify(ref this.transitStopId, value, () => this.TransitStopId);
            }
        }

        public string TransitTerminus
        {
            get
            {
                return this.transitTerminus;
            }
            set
            {
                this.SetPropertyAndNotify(ref this.transitTerminus, value, () => this.TransitTerminus);
            }
        }

        public double? TravelDistance
        {
            get
            {
                return this.travelDistance;
            }
            set
            {
                this.SetPropertyAndNotify(ref this.travelDistance, value, () => this.TravelDistance);
            }
        }

        public double? TravelDuration
        {
            get
            {
                return this.travelDuration;
            }
            set
            {
                this.SetPropertyAndNotify(ref this.travelDuration, value, () => this.TravelDuration);
            }
        }

        public string TravelMode
        {
            get
            {
                return this.travelMode;
            }
            set
            {
                this.SetPropertyAndNotify(ref this.travelMode, value, () => this.TravelMode);
            }
        }

        public ObservableCollection<RouteWarning> Warnings
        {
            get
            {
                return this.warnings;
            }
            set
            {
                this.SetPropertyAndNotify(ref this.warnings, value, () => this.Warnings);
            }
        }

        public bool HasInstruction
        {
            get { return this.instruction != null; }
        }

        public bool HasManeuverPoint
        {
            get { return this.maneuverPoint != null; }
        }

        public bool HasDetails
        {
            get { return this.details != null && this.details.Count != 0; }
        }

        public bool HasHints
        {
            get { return this.hints != null && this.hints.Count != 0; }
        }

        public bool HasSigns
        {
            get { return this.signs != null && this.signs.Count != 0; }
        }

        public bool HasTransitLine
        {
            get { return this.transitLine != null && this.transitLine.Count != 0; }
        }

        public bool HasWarnings
        {
            get { return this.warnings != null && this.warnings.Count != 0; }
        }

        public ItineraryItem ShallowClone()
        {
            var item = new ItineraryItem
            {
                CompassDirection = this.compassDirection,
                Exit = this.exit,
                IconType = this.iconType,
                Instruction = this.instruction,
                ManeuverPoint = this.maneuverPoint,
                SideOfStreet = this.sideOfStreet,
                Time = this.time,
                TollZone = this.tollZone,
                towardsRoadName = this.towardsRoadName,
                TransitStopId = this.transitStopId,
                TransitTerminus = this.transitTerminus,
                TravelDistance = this.travelDistance,
                TravelDuration = this.travelDuration,
                TravelMode = this.travelMode,
                Details = this.details,
                ChildItineraryItems = this.childItineraryItems,
                Hints = this.hints,
                Signs = this.signs,
                TransitLine = this.transitLine,
                Warnings = this.warnings,
            };

            return item;
        }

        public ItineraryItem DeepClone()
        {
            var item = new ItineraryItem
            {
                CompassDirection = this.compassDirection,
                Exit = this.exit,
                IconType = this.iconType,
                SideOfStreet = this.sideOfStreet,
                Time = this.time,
                TollZone = this.tollZone,
                towardsRoadName = this.towardsRoadName,
                TransitStopId = this.transitStopId,
                TransitTerminus = this.transitTerminus,
                TravelDistance = this.travelDistance,
                TravelDuration = this.travelDuration,
                TravelMode = this.travelMode,
            };

            if (this.HasInstruction)
                item.Instruction = this.instruction.DeepClone();

            if (this.HasManeuverPoint)
                item.ManeuverPoint = new GeoCoordinate(this.maneuverPoint.Latitude, this.maneuverPoint.Longitude);

            if (this.HasDetails)
            {
                var copy = new ObservableCollection<RouteDetail>();
                this.details.ForEach(d => copy.Add(d.DeepClone()));

                item.Details = copy;
            }

            if (this.HasHints)
            {
                var copy = new ObservableCollection<RouteHint>();
                this.hints.ForEach(h => copy.Add(h.DeepClone()));

                item.Hints = copy;
            }

            if (this.HasSigns)
            {
                var copy = new ObservableCollection<string>();
                this.signs.ForEach(copy.Add);

                item.Signs = copy;
            }

            if (this.HasTransitLine)
            {
                var copy = new ObservableCollection<RouteTransitLine>();
                this.transitLine.ForEach(t => copy.Add(t.DeepClone()));

                item.TransitLine = copy;
            }

            if (this.HasWarnings)
            {
                var copy = new ObservableCollection<RouteWarning>();
                this.warnings.ForEach(w => copy.Add(w.DeepClone()));

                item.Warnings = copy;
            }

            return item;
        }
    }
}